#ifndef RBT_TEXTWRITER_INCLUDE
#define RBT_TEXTWRITER_INCLUDE

#include "typedef.h"
#include "Encoding.h"
#include "IO\FileStream.h"




RETURNCODE E_STREAMWRITER_INVALIDSTREAM  = -25001;
RETURNCODE E_STREAMWRITER_CREATEENCODING = -25002;
RETURNCODE E_STREAMWRITER_CANWRITE       = -25003;
RETURNCODE E_STRRAMWRITER_ALLOCBUFFER    = -25004;

class StreamWriter
{
	Stream*   m_stream;
	Encoding* m_encoding;
	bool      m_userStream;
	bool      m_userEncoding;

	byte*     m_bytes;
	uint32    m_byteSize;

	byte*     m_reline;
	uint32    m_relineSize;

	byte*     m_saves;
	uint32    m_savePos;

	const static uint32 MAX_WRITE_COUNT = 8*KB;
	const static uint32 MAX_WRITE_SIZE  = 32*KB;

	int32 Initalize()
	{
		m_stream       = 0;
		m_encoding     = 0;
		m_userStream   = true;
		m_userEncoding = true;
		m_bytes        = 0;
		m_byteSize     = 0;
		m_reline       = 0;
		m_relineSize   = 0;
		m_savePos      = 0;
		m_saves        = 0;

		return R_SUCCEED;
	}

	int32 WriteStream( byte* bytes , uint32 byteSize )
	{
		uint32 freeSize = byteSize;
		int32  code     = R_SUCCEED;

		while( freeSize > 0 )
		{
			uint32 freeSave = MAX_WRITE_SIZE - m_savePos;
			if(freeSave > 0 )
			{
				uint32 copySize = freeSave>freeSize?freeSize:freeSave;
				uint32 offset   = byteSize - freeSize;
				memcpy_s( (void*)(m_saves + m_savePos) , freeSave , (void*)(bytes + offset), copySize );
				m_savePos += copySize;
				freeSize  -= copySize;
			}

			if( m_savePos >= MAX_WRITE_SIZE )
			{
				code = Flush();
				if(code != R_SUCCEED)
					break;
			}
		}

		return code;
	}

	int32 Write( ctchar* chars , uint32 charSize )
	{
		int32 code = R_SUCCEED;

		uint32 byteSize = 0;

		code = m_encoding->GetByteSize( byteSize );

		if( code != R_SUCCEED )
			return code;

		uint32 readSize  = MAX_WRITE_COUNT / byteSize;

		uint32 readCount = 0;
		uint32 fileWrite = 0;

		while( readCount < charSize )
		{
			ctchar* readChar  = chars + readCount;
			uint32  useChars  = 0;
			uint32  useBytes  = 0;
			uint32  freeSize  = charSize - readCount;
			uint32  encodeSize= readSize>freeSize?freeSize:readSize;

			code = m_encoding->Encode( readChar , encodeSize , m_bytes , m_byteSize , useChars , useBytes );

			if(code != R_SUCCEED)
				break;

			WriteStream( m_bytes , useBytes );

			if( code != R_SUCCEED )
				break;

			readCount += useChars;
		}

		return code;
	}

public:

	StreamWriter()
	{
		Initalize();
	}

	int32 Initalize( cstring& path )
	{
		return Initalize( path , 0 );
	}

	int32 Initalize( cstring& path , Encoding* encoding )
	{
		int32 code = R_SUCCEED;
		FileStream* fileStream = new FileStream();

		code = fileStream->Open( path.c_str() , FileStream::CREATE | FileStream::CREATE | FileStream::SHARE_READ);

		if(code != R_SUCCEED)
			return code;

		return Initalize(fileStream , encoding );
	}

	int32 Initalize( Stream* stream )
	{
		return Initalize( stream , 0 );
	}

	int32 Initalize( Stream* stream , Encoding* encoding )
	{
		if( stream == 0 )
			return E_STREAMWRITER_INVALIDSTREAM;
		m_stream = stream;

		if( m_stream->CanWrite() != R_SUCCEED )
			return E_STREAMWRITER_CANWRITE;

		if( encoding == 0 )
		{
			m_encoding = Encoding::Create(ENCODING_UTF8);
			if( m_encoding == 0 )
				return E_STREAMWRITER_CREATEENCODING;
			m_userEncoding = false;
		}
		else
		{
			m_encoding = encoding;
		}

		int32 code = R_SUCCEED;

		uint32 byteSize = 0;
		code = m_encoding->GetByteSize(byteSize);

		if(code != R_SUCCEED)
			return code;

		uint32 allocSize = byteSize * (MAX_WRITE_COUNT + 1 );

		m_bytes = (byte*)malloc( allocSize );

		if(m_bytes == 0)
			return E_STRRAMWRITER_ALLOCBUFFER;

		m_byteSize = allocSize;

		uint32 allocReline = byteSize * 3;

		m_reline = (byte*)malloc( allocReline );

		if( m_reline == 0 )
			return E_STRRAMWRITER_ALLOCBUFFER;

		uint32 useCharSize = 0;
		uint32 useByteSize = 0;

		code = m_encoding->Encode( _RBT("\r\n") , 2 , m_reline , allocReline , useCharSize , useByteSize );

		if(code != R_SUCCEED)
			return code;

		m_relineSize = useByteSize;

		m_saves   = (byte*)malloc( MAX_WRITE_SIZE );
		if(m_saves == 0)
			return E_STRRAMWRITER_ALLOCBUFFER;

		m_savePos = 0;

		return R_SUCCEED;
	}

	int32 Flush()
	{
		uint32 fileWrite = 0;
		int32  code      = R_SUCCEED;
		code = m_stream->Write( m_saves , m_savePos , fileWrite );
		m_savePos = 0;
		return code;
	}



	int32 WriteLine( cstring& value )
	{
		int32 code = Write(value);
		if( code != R_SUCCEED )
			return code;
		return WriteStream( m_reline , m_relineSize );
	}

	int32 Write( cstring& value )
	{
		ctchar* chars    = value.c_str();
		uint32 charSize  = value.size();
		return Write( chars , charSize );
	}

	int32 Write( ctchar& value )
	{
		return Write( &value , 1 );
	}

	int32 Uninitalize()
	{
		int retCode = R_SUCCEED;
		int32 code  = R_SUCCEED;

		code = Flush();
		if(code != R_SUCCEED)
			retCode = code;

		if( !m_userEncoding )
		{
			Encoding::Destroy( m_encoding );
		}

		if( !m_userStream )
		{
			code = m_stream->Close();
			delete m_stream;
			if(code != R_SUCCEED)
				retCode = code;
		}

		if( m_saves != 0 )
			free(m_saves);

		if( m_reline != 0 )
			free(m_reline);

		if( m_bytes != 0 )
			free(m_bytes);

		Initalize();

		return retCode;
	}

};

#endif